#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <iostream>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <string>
#include <unistd.h>
#include <cstring>
enum
{
    SOCK_CREATE_ERROR = 1,
    SOCK_BIND_ERROR,
    SOCK_LISTEN_ERROR,
    EPOLL_CREATE_ERROR,
    EPOLL_CTL_ERROR
};
class EpollServer
{
    const static int defaultfd = -1;
    const static int gbacklog = 16;
    const static int NUM = 64;
    public:
        EpollServer(uint16_t port) :
        _port(port) , _isrunning(false) ,
        _listensock(defaultfd) , _epfd(-1)
        {}
        void InitEpollServer()
        {
            //创建套接字
            _listensock = ::socket(AF_INET,SOCK_STREAM,0);
            if(_listensock < 0)
            {
                //创建失败
                std::cerr << "create listensocket error!" << std::endl;
                exit(SOCK_CREATE_ERROR);
            }
            //套接字创建成功，下一步是绑定套接字
            sockaddr_in addr;
            addr.sin_addr.s_addr = INADDR_ANY;
            addr.sin_family = AF_INET;
            addr.sin_port = htons(_port);
            int n = ::bind(_listensock,reinterpret_cast<sockaddr*>(&addr),sizeof(addr));
            if(n < 0)
            {
                //绑定失败
                std::cerr << "Socket Bind Error!" << std::endl;
                exit(SOCK_BIND_ERROR);
            }
            //绑定成功，下一步是设置套接字为监听状态
            n = ::listen(_listensock,gbacklog);
            if(n < 0)
            {
                //设置失败
                std::cerr << "Set Listen Status Error!" << std::endl;
                exit(SOCK_LISTEN_ERROR);
            }
            //设置成功，listen套接字初始化完毕，接下来创建epoll模型
            _epfd = ::epoll_create(128);
            if(_epfd < 0)
            {
                //epoll模型创建失败
                std::cerr << "Epoll Create Error!" << std::endl;
                exit(EPOLL_CREATE_ERROR);
            }
            //创建epoll模型成功，把listensock添加进epoll模型中
            struct epoll_event ep;
            ep.data.fd = _listensock;
            ep.events = EPOLLIN;
            n = ::epoll_ctl(_epfd,EPOLL_CTL_ADD,_listensock,&ep);
            if(n < 0)
            {
                //添加失败
                std::cerr << "Epoll Add Fd Error!" << std::endl;
                exit(EPOLL_CTL_ERROR);
            }
            //添加成功，整个初始化已完成
        }
        void AcceptClient()
        {
            sockaddr_in peer;
            socklen_t len;
            int sockfd = ::accept(_listensock,reinterpret_cast<sockaddr*>(&peer),&len);
            if(sockfd < 0)
            {
                //accept失败，一般不要终止程序，打印一个日志即可
                std::cerr << "Server Accept Error!" << std::endl;
            }
            else 
            {
                //accept成功
                //把新得到的，用于与客户端通信的套接字放入到epoll模型中
                struct epoll_event ep;
                ep.data.fd = sockfd;
                ep.events = EPOLLIN;
                int n = ::epoll_ctl(_epfd,EPOLL_CTL_ADD,sockfd,&ep);
                if(n < 0)
                {
                    std::cout << "Epoll Ctl Error!" << std::endl;
                }
            }
        }
        void Service(int sockfd)
        {
            char inbuffer[1024];
            memset(inbuffer,0,sizeof(inbuffer));
            ssize_t n = ::recv(sockfd,inbuffer,1024,0);
            if(n > 0)
            {
                //读取成功
                //构建新的buffer
                std::string outbuffer;
                outbuffer += "[Server Say#] ";
                outbuffer += inbuffer;
                //发送给客户端
                ::send(sockfd,outbuffer.c_str(),outbuffer.size(),0);
            }
            else if(n == 0)
            {
                //对方把连接关闭
                //1、把套接字从epoll中删除。2、把套接字关闭
                ::epoll_ctl(_epfd,EPOLL_CTL_DEL,sockfd,nullptr);
                ::close(sockfd);
                std::cout << "client close fd!" << std::endl;
            }
            else 
            {
                //读取采用的是阻塞读取方式，这意味着走到这里必定是recv发生错误
                std::cerr << "Eecv Error!" << std::endl;
            }
        }
        void Start()
        {
            while(true)
            {
                //阻塞方式获取就绪的fd
                int n = epoll_wait(_epfd,_event,NUM,-1);
                //获取到至少一个就绪的fd
                for(int i = 0 ; i < n ; ++i)
                {
                    if(_event[i].events & EPOLLIN)
                    {
                        //暂时epoll服务器只关心读事件
                        if(_event[i].data.fd == _listensock)
                        {
                            //如果是listen套接字，就让他去处理客户端的连接请求
                            AcceptClient();
                        }
                        else 
                        {
                            Service(_event[i].data.fd);
                        }
                    }
                }
            }
        }
    private:
        int _listensock;
        struct epoll_event _event[NUM];
        uint16_t _port;
        int _epfd;
        bool _isrunning;
};